

/*

实现功能
功能说明, o表示已经确认,z表示未测试,n表示未实现,x表示未通过,w需要实现,但需要等待,s由软件应该自行实现功能
+-------------------            已经实现
|+------------------            iverilog仿真测试通过
||+-----------------            硬件测试
|||+----------------            建议软件实现
||||
ooz                     cmd_en          ;发送命令
ooz                     need_rsp
ooz                     long_rsp
ooz                     data_en
ooz                     data_write
ooz                     timeout
ooz                     data_crc_ok
ooz                     rsp_crc_ok
ooz                     long_rsp_crc_ok
nnn                     busy

zzz                     fifo_send*
zzz                     fifo_recv*
zzz                     fifo_send_rst
zzz                     fifo_recv_rst

*/

module sdio_fifo_top(
`ifdef SIM
    sdio_cmd_test,
    sdio_dat_test,
`endif

    clk,
    rstn,
    sdio_dat,
    sdio_cmd,
    sdio_clk,

    cmd,
    argu,
    cmd_en,
    rsp,
    need_rsp,
    long_rsp,
    data_en,
    data_write,
    block_size,
    data_len,
    data_done,
    cmd_done,
    cmd_len,
    rsp_len,
    cmd_init,

    fifo_send_emty,
    fifo_send_data_i,
    fifo_send_rden,
    fifo_send_rst,

    fifo_recv_data_o,
    fifo_recv_wren,
    fifo_recv_full,
    fifo_recv_rst,

    data_crc_ok,
    rsp_crc_ok,
    dat_crc_fail,
    rsp_crc_fail,
    dat_write_ok,
    dat_write_busy,
    busy,
    clk_div,

    timeout_time,
    timeout
);

    input wire clk;
    input wire rstn;
    inout wire[3:0] sdio_dat;
    inout wire      sdio_cmd;
    output wire busy;
    output reg sdio_clk = 0;
    reg sdio_cmd_oe = 1'b0;
    reg sdio_cmd_o = 0;
    reg sdio_dat_oe = 0;
    reg [3:0] sdio_dat_o = 0;
    input [15:0] block_size;
    input [31:0] data_len;
    output reg  cmd_done;
    output wire dat_write_ok;
    output wire dat_write_busy;
    output reg  dat_crc_fail;
    output reg  rsp_crc_fail;
    input [7:0]     cmd_len;
    input [15:0]    clk_div;
`ifdef SIM
    input sdio_cmd_test;
    input[3:0] sdio_dat_test;
    assign sdio_cmd = (sdio_cmd_oe)?sdio_cmd_o:sdio_cmd_test;
    assign sdio_dat = (sdio_dat_oe)?sdio_dat_o:sdio_dat_test;
`else
    assign sdio_cmd = (sdio_cmd_oe)?sdio_cmd_o:1'bz;
    assign sdio_dat = (sdio_dat_oe)?sdio_dat_o:4'bz;
`endif
    input data_en;
    input data_write;
    input [31:0] timeout_time;
    input cmd_init;
    output wire  timeout;
    reg [31:0]   tick;
    reg          tick_reset_trg,tick_reset_trg_nt;
    wire         tick_reset = tick_reset_trg^tick_reset_trg_nt;
    reg          timeout_set = 0;

    assign       timeout = (tick>timeout_time) && timeout_set;

    //FIFO模块
    input                   fifo_send_emty;
    output wire             fifo_send_rden;
    input wire[31:0]        fifo_send_data_i;
    output wire[31:0]       fifo_recv_data_o;// = recv_data;
    input fifo_recv_wren,fifo_recv_full;
    output wire     data_crc_ok;
    output wire     rsp_crc_ok;
    output wire data_done;
    output reg fifo_send_rst,fifo_recv_rst;

    input [5:0]         cmd;
    input               cmd_en;
    input [31:0]        argu;
    output reg[255:0]   rsp;
    input               need_rsp;
    input               long_rsp;
    input [7:0]         rsp_len;


    reg [5:0]       cur_state;
    reg [5:0]       nxt_state;
    localparam      state_idle      = 0;
    localparam      state_cmd_init  = 1;
    localparam      state_cmd       = 2;
    localparam      state_rsp_wait  = 3;
    localparam      state_rsp       = 4;
    localparam      state_dat_wait  = 5;
    localparam      state_dat       = 6;
    localparam      state_dat_write = 7;
    localparam      state_dat_write_wait = 8;
    localparam      state_dat_write_chk  = 9;
    localparam      state_dat_block      = 10;
    //////////////////////////////
    //状态机参数
    reg cmd_start;
    reg cmd_send_done;
    reg cmd_need_rsp;
    reg cmd_rsp_wait;
    reg cmd_rsp_done;
    reg cmd_dat_wait;
    reg cmd_dat_done;
    reg dat_check_done;
    reg [15:0] wait_remaining;
    //////////////////////////////
    //操作
    reg [47:0] cmd_shift;
    reg [8:0]  cmd_remaining;
    reg [15:0] dat_remaining;
    reg [31:0] total_dat_remaining;
    reg [31:0] recv_data;
    wire       data_start_flg =
        //total_dat_remaining[31]&&dat_remaining==40;
        cur_state==state_dat;
    assign     data_done = dat_remaining==18;

    //////////////////////////////
    //输出状态
    assign   dat_write_busy =
        cur_state==state_dat_write||
        cur_state==state_dat_write_chk||
        cur_state==state_dat_write_wait;
    assign   busy = cur_state!=state_idle;

    assign dat_write_ok = sdio_dat_write_chk[4:2]==3'b010;
    //////////////////////////////
    //fifo模块
    //接收
    //reg[8:0] fifo_recv_wren_shift;
    //assign fifo_recv_wren = fifo_recv_wren_shift & (fifo_recv_wren_shift[8]^fifo_recv_wren_trg);    //只执行一个周期
    reg    fifo_recv_wren_trg,fifo_recv_wren_trg_nt;
    assign      fifo_recv_wren = fifo_recv_wren_trg_nt ^ fifo_recv_wren_trg;
    assign      fifo_recv_data_o = recv_data;
    reg[3:0]    fifo_recv_len = 0;
    reg[15:0]   clk_div_counter = 0;

    task task_fifo_init;
    begin
        //fifo_recv_wren_shift <= 0;
        //fifo_recv_wren_trg   <= 0;
    end
    endtask

    task task_fifo_wren_en;begin
        //fifo_recv_wren_shift <= 1;
        fifo_recv_rst        <= 0;
    end
    endtask

    // task task_fifo_wren_update;
    //     fifo_recv_wren_shift <= {fifo_recv_wren_shift[7],fifo_recv_wren_shift[6:0],fifo_recv_wren_shift[7]};
    // endtask

    //发送
    //reg[8:0]    fifo_send_rden_shift = 0;
    reg[3:0]    fifo_send_data_len = 0;
    reg         fifo_send_data_has = 0;
    reg         fifo_send_rden_trg = 0;
    reg         fifo_send_rden_trg_nt = 0;
    assign      fifo_send_rden = /*fifo_send_rden_shift[0] &&*/ !fifo_send_emty && (fifo_send_rden_trg_nt^fifo_send_rden_trg);
    reg[31:0]   send_dat;

    //////////////////////////////
    //计算效验码

    reg[5:0] sdio_dat_write_chk = 0;
    reg      sdio_dat_crc_rst =  0;
    reg      sdio_dat_crc_calc_trg,sdio_dat_crc_calc_trg_nt;
    wire     sdio_dat_crc_calc = sdio_dat_crc_calc_trg^sdio_dat_crc_calc_trg_nt;

     wire[15:0]  sdio_crc_o0;
     crc16_sig      u_crc_d0(
        .dat_i  ( sdio_dat[0]),
        .crc_in (sdio_crc_o0),
        .crc_o  (sdio_crc_o0),
        .calc(sdio_dat_crc_calc),
      .rst    (sdio_dat_crc_rst),
        .clk    (clk)
    );

    wire[15:0]   sdio_crc_o1;
    crc16_sig       u_crc_d1(
        .dat_i  ( sdio_dat[1]),
        .crc_in (sdio_crc_o1),
        .crc_o  (sdio_crc_o1),
        .calc(sdio_dat_crc_calc),
        .rst  (sdio_dat_crc_rst),
        .clk    (clk)
    );

    wire[15:0]   sdio_crc_o2;
    crc16_sig       u_crc_d2(
        .dat_i  ( sdio_dat[2]),
        .crc_in (sdio_crc_o2),
        .crc_o  (sdio_crc_o2),
        .calc(sdio_dat_crc_calc),
        .rst  (sdio_dat_crc_rst),
        .clk    (clk)
    );

    wire[15:0]   sdio_crc_o3;
    crc16_sig       u_crc_d3(
        .dat_i  ( sdio_dat[3]),
        .crc_in (sdio_crc_o3),
        .crc_o  (sdio_crc_o3),
        .calc(sdio_dat_crc_calc),
        .rst  (sdio_dat_crc_rst),
        .clk    (clk)
    );

    reg [15:0]  recv_data_crc0;
    reg [15:0]  recv_data_crc1;
    reg [15:0]  recv_data_crc2;
    reg [15:0]  recv_data_crc3;

    wire recv_data_crc_ok0 = recv_data_crc0==sdio_crc_o0;
    wire recv_data_crc_ok1 = recv_data_crc1==sdio_crc_o1;
    wire recv_data_crc_ok2 = recv_data_crc2==sdio_crc_o2;
    wire recv_data_crc_ok3 = recv_data_crc3==sdio_crc_o3;

    assign data_crc_ok =
        recv_data_crc_ok0&
        recv_data_crc_ok1&
        recv_data_crc_ok2&
        recv_data_crc_ok3;

    assign rsp_crc_ok = rsp[7:0]==rsp_crc7;
    reg[3:0] crc_check = 0;

    /////////////////////////////

    always @(*) begin
        nxt_state   = state_idle;
        if(cmd_en&&cmd_init)begin
            //初始化设置
            nxt_state = state_cmd_init;
        end
        else
        case (cur_state)
            state_idle      :nxt_state = cmd_en?state_cmd_init:cur_state ;
            state_cmd_init  :nxt_state = cmd_start?state_cmd  :cur_state ;
            state_cmd       :nxt_state = (cmd_send_done&&cmd_need_rsp)?
                                            state_rsp_wait:
                                            (cmd_send_done?state_idle:cur_state);
            state_rsp_wait  :nxt_state = !cmd_rsp_wait?state_rsp:(timeout?state_idle:cur_state);        //需要超时设置
            state_rsp       :nxt_state =
                                        cmd_rsp_done?
                                        (data_en?(data_write?state_dat_write_wait:state_dat_wait):state_idle)
                                        :cur_state;
            state_dat_wait  :nxt_state = !cmd_dat_wait?state_dat:(timeout?state_idle:cur_state);        //需要超时设置
            state_dat       :nxt_state = cmd_dat_done?state_dat_block:cur_state;
            state_dat_write :nxt_state = cmd_dat_done?state_dat_write_chk:cur_state;
            state_dat_write_wait: nxt_state = wait_remaining==0?state_dat_write:cur_state;
            state_dat_write_chk :nxt_state =  (timeout|dat_check_done)?state_dat_block:cur_state;            //需要超时设置
            state_dat_block     :nxt_state =
                                                timeout?state_idle:         //超时的时候直接跳到空闲状态
                                                (!total_dat_remaining[31]?(data_write?state_dat_write_wait:state_dat_wait):state_idle)
                                                ;
            default:nxt_state = state_idle;
        endcase
    end

    always @(posedge clk or negedge rstn) begin
        if(!rstn)begin
            cmd_start       <= 0;
            cmd_send_done   <= 0;
            cmd_need_rsp    <= 0;
            cmd_rsp_wait    <= 1;
            cmd_rsp_done    <= 0;
            cur_state       <= state_idle;
            sdio_clk        <= 1;
            sdio_dat_crc_calc_trg_nt <= 0;
            sdio_dat_crc_calc_trg <= 0;
            tick            <= 0;
            recv_data_crc0 <= 0;
            recv_data_crc1 <= 0;
            recv_data_crc2 <= 0;
            recv_data_crc3 <= 0;
            wait_remaining <= 0;
            dat_remaining  <= 0;
            cmd_remaining  <= 0;
            cmd_dat_done   <= 0;
            cmd_dat_wait   <= 0;
            dat_check_done <= 0;
            recv_data      <= 0;
            cmd_done       <= 0;
            fifo_send_rden_trg   <= 0;
            //fifo_send_rden_shift <= 0;
            fifo_recv_wren_trg_nt   <= 0;
            fifo_recv_wren_trg      <= 0;
            send_dat       <= 0;
            rsp_crc_fail   <= 0;
            crc_check      <= 0;
            task_fifo_init;
            clk_div_counter <= 0;
        end
        else begin
            cur_state               <= nxt_state;
            sdio_dat_crc_calc_trg   <= sdio_dat_crc_calc_trg_nt;
            tick_reset_trg          <= tick_reset_trg_nt;
            fifo_recv_wren_trg      <= fifo_recv_wren_trg_nt;
            // if(!fifo_send_emty)         //不为空才触发
            //     fifo_send_rden_trg <= fifo_send_rden_shift[0];
            fifo_send_rden_trg      <= fifo_send_rden_trg_nt;


            if(tick_reset)
                tick <= 0;
            else if(tick<=timeout_time)
                tick <= tick + 1;

            if(clk_div==0)
                clk_div_counter <= 0;
            else if(clk_div_counter>clk_div)
                clk_div_counter <= 0;
            else
                clk_div_counter <= clk_div_counter + 1;


            // if(clk_div_counter>clk_div)begin
            //     ;
            // end
            // else if(clk_div_counter==clk_div)
            case (cur_state)
                state_cmd_init:begin
                    cmd_shift       <= cmd_seq;
                    cmd_start       <= 1;
                    cmd_send_done   <= 0;
                    cmd_done        <= 0;
                    if(cmd_init)
                        cmd_remaining   <= 256;
                    else
                        cmd_remaining   <= cmd_len;
                    cmd_rsp_done    <= 0;
                end
                state_cmd:begin

                    timeout_set         <= 0;
                    cmd_start           <= 0;
                    //sdio_clk            <= (~sdio_clk || cmd_remaining==0) && (clk_div_counter==clk_div);
                    sdio_clk            <= (sdio_clk ^ (clk_div_counter==clk_div)) || ((cmd_remaining==0)&&(clk_div_counter==clk_div));
                    cmd_rsp_wait        <= need_rsp;       //需要rsp接收
                    cmd_dat_wait        <= data_en;        //开启数据传输
                    tick_reset_trg_nt   <= tick_reset_trg_nt ^ cmd_need_rsp;        //rsp超时设置
                    fifo_send_data_len  <= 0;
                    fifo_send_data_has  <= 0;
                    crc_check           <= 4;
                    rsp_crc_fail        <= 0;
                    if(sdio_clk && (clk_div_counter==clk_div))begin
                        if(cmd_remaining>0)
                            cmd_remaining <= cmd_remaining - 1;
                        cmd_send_done <= cmd_remaining==0;
                        cmd_done      <= need_rsp?0:(cmd_remaining<=1);
                        sdio_cmd_oe   <= cmd_remaining!=0;
                        sdio_cmd_o    <= cmd_shift[47];
                        cmd_need_rsp  <= need_rsp;
                        cmd_shift     <= {cmd_shift[46:0],1'b1};
                    end
                end
                state_rsp_wait:begin
                    timeout_set         <= 1;
                    sdio_clk            <= sdio_clk ^ (clk_div_counter==clk_div);
                    rsp                 <= 0;
                    cmd_remaining       <= rsp_len - 1;
                    if(sdio_clk && (clk_div_counter==clk_div))
                        cmd_rsp_wait    <= sdio_cmd;
                end
                state_rsp:begin
                    timeout_set         <= 0;
                    sdio_clk            <= (sdio_clk ^ (clk_div_counter==clk_div)) || cmd_remaining<2;

                    //data状态处理,这个状态已经不需要等待rsp,需要处理超时
                    wait_remaining      <= 5;           //data write的等待
                    tick_reset_trg_nt   <= tick_reset_trg_nt^cmd_dat_wait;
                    if(data_en)begin
                        //数据启用
                        //fifo_recv_rst   <= 1;
                        total_dat_remaining <= data_len - {16'd0,block_size} - 1;
                    end

                    //////////////////////////////////

                    if(sdio_clk && (clk_div_counter==clk_div))begin
                        cmd_remaining   <= cmd_remaining-1;
                        cmd_rsp_done    <= cmd_remaining<=1;
                        cmd_done        <= cmd_remaining<=1;
                        if(cmd_remaining>0)
                            rsp             <= {rsp[254:0],sdio_cmd};
                    end
                    sdio_dat_crc_rst    <= 1;       //crc重置
                end
                state_dat_wait:begin
                    timeout_set         <= 1;
                    sdio_dat_oe         <= 0;
                    sdio_dat_crc_rst    <= 0;       //准备计算crc
                    sdio_clk            <= (clk_div_counter==clk_div)^sdio_clk;
                    dat_remaining       <= {block_size,1'b0} + 18 - 1;
                    cmd_dat_done        <= 0;

                    //fifo重启关闭
                    fifo_recv_rst       <= 0;
                    fifo_recv_len       <= 0;

                    if(sdio_clk && (clk_div_counter==clk_div))begin
                        cmd_dat_wait    <= sdio_dat=='hf;
                        sdio_dat_crc_calc_trg_nt <= sdio_dat_crc_calc_trg_nt^(sdio_dat!='hf);
                    end
                end
                state_dat:begin
                    timeout_set         <= 0;
                    fifo_recv_rst       <= 0;
                    sdio_clk            <= ((clk_div_counter==clk_div)^sdio_clk) || dat_remaining<2;
                    if(sdio_clk && (clk_div_counter==clk_div))begin



                        if(dat_remaining>15)begin
                            if(fifo_recv_len<7)
                                fifo_recv_len   <= fifo_recv_len + 1;
                            else begin
                                fifo_recv_len   <= 0;
                            end

                            fifo_recv_wren_trg_nt <= fifo_recv_wren_trg_nt^(fifo_recv_len==7);
                        end

                        dat_remaining   <=  dat_remaining-1;
                        cmd_dat_done    <= dat_remaining==1;
                        recv_data       <= {recv_data[27:0],sdio_dat};

                        if(dat_remaining>1)begin
                            //停止读取
                            recv_data_crc0 <= {recv_data_crc0[14:0],sdio_dat[0]};
                            recv_data_crc1 <= {recv_data_crc1[14:0],sdio_dat[1]};
                            recv_data_crc2 <= {recv_data_crc2[14:0],sdio_dat[2]};
                            recv_data_crc3 <= {recv_data_crc3[14:0],sdio_dat[3]};
                        end

                        //触发一次crc16计算
                        sdio_dat_crc_calc_trg_nt <= sdio_dat_crc_calc_trg_nt ^ ($signed(dat_remaining)>18);
                    end
                end
                state_dat_write_wait:begin
                    cmd_dat_done        <= 0;
                    timeout_set         <= 0;
                    tick_reset_trg_nt   <= ~tick_reset_trg_nt;
                    sdio_clk            <= ((clk_div_counter==clk_div)^sdio_clk) || wait_remaining < 1;
                    sdio_dat_oe         <= 1;
                    dat_remaining       <= {block_size,1'b0} + 18 - 1;
                    sdio_dat_crc_rst    <= 0;       //准备计算crc
                    if(sdio_clk && (clk_div_counter==clk_div))begin
                        //被设置为0时,为开始标志
                        sdio_dat_o          <= wait_remaining>1?'hf:0;

                        if(!fifo_send_emty)begin
                            //不为空时,开始发送数据
                            wait_remaining          <= wait_remaining-1;
                            //fifo_send_rden_shift    <= 1;
                            if(fifo_send_data_len==0)
                                fifo_send_rden_trg_nt   <= ~fifo_send_rden_trg_nt;
                        end
                    end

                    if(fifo_send_rden)begin
                        fifo_send_data_len      <= 8;
                        send_dat                <= {fifo_send_data_i[7:0],fifo_send_data_i[15:8],fifo_send_data_i[23:16],fifo_send_data_i[31:24]};
                    end

                end
                state_dat_write:begin
                    timeout_set <= 0;
                    if(fifo_send_data_len>8)begin
                        sdio_clk <= 1;
                        fifo_send_data_len <= fifo_send_data_len - 1;
                    end
                    else if(
                        (fifo_send_data_len>0)||
                        (dat_remaining<=17)
                    )begin
                        sdio_clk            <=
                                            ((clk_div_counter==clk_div) ^ sdio_clk) ||
                                            $signed(dat_remaining)<0 ||
                                            dat_remaining==17;          //效验码时,停止一个周期
                        sdio_dat_oe         <= data_write;
                        sdio_dat_write_chk  <= ~0;
                        cmd_dat_wait        <= 0;
                        dat_check_done      <= 0;
                        if(sdio_clk && (clk_div_counter==clk_div)) begin
                            dat_remaining   <=  dat_remaining-1;
                            cmd_dat_done    <= dat_remaining==0;

                            if(dat_remaining>16)begin
                                //sdio_dat_o      <= 'hf;         //暂时测试的数据
                                if(fifo_send_data_len==1 && fifo_send_data_has)begin
                                    fifo_send_data_len    <= 8;
                                    fifo_send_data_has    <= 0;
                                end
                                else if(dat_remaining>25 && fifo_send_data_len==2 && !fifo_send_emty)begin
                                    //需要提前两拍获得fifo
                                    fifo_send_rden_trg_nt <= ~fifo_send_rden_trg_nt;
                                    fifo_send_data_len   <= fifo_send_data_len - 1;
                                    fifo_send_data_has   <= 1;
                                end
                                else if(fifo_send_data_len>0)
                                    fifo_send_data_len <= fifo_send_data_len - 1;



                                if(
                                    //fifo_send_rden_shift[0]
                                    fifo_send_data_len==8
                                )begin
                                    sdio_dat_o          <= fifo_send_data_i[7:4];
                                    send_dat            <= {fifo_send_data_i[3:0],fifo_send_data_i[15:8],fifo_send_data_i[23:16],fifo_send_data_i[31:24],4'hf};
                                end
                                else begin
                                    sdio_dat_o  <= send_dat[31:28];
                                    send_dat    <= {send_dat[27:0],4'hf};
                                end

                                //fifo_send_rden_shift <= {fifo_send_rden_shift[7],fifo_send_rden_shift[6:0],fifo_send_rden_shift[7]};

                                if(dat_remaining==17)begin
                                    recv_data_crc0  <= sdio_crc_o0;
                                    recv_data_crc1  <= sdio_crc_o1;
                                    recv_data_crc2  <= sdio_crc_o2;
                                    recv_data_crc3  <= sdio_crc_o3;
                                end
                            end
                            else begin
                                //处理效验码
                                sdio_dat_o <= {
                                    recv_data_crc0[15],
                                    recv_data_crc1[15],
                                    recv_data_crc2[15],
                                    recv_data_crc3[15]
                                };
                                recv_data_crc0 <= {recv_data_crc0[14:0],1'b1};
                                recv_data_crc1 <= {recv_data_crc1[14:0],1'b1};
                                recv_data_crc2 <= {recv_data_crc2[14:0],1'b1};
                                recv_data_crc3 <= {recv_data_crc3[14:0],1'b1};
                            end
                            sdio_dat_crc_calc_trg_nt <= sdio_dat_crc_calc_trg_nt ^ ($signed(dat_remaining)>17);
                        end
                    end
                    else if(~fifo_send_emty)begin
                        sdio_clk <= 1;
                        if($signed(dat_remaining)>0)begin
                            fifo_send_rden_trg_nt   <= ~fifo_send_rden_trg_nt;
                            fifo_send_data_len      <= 10;       //需要额外几个节拍,最小为10
                        end
                        else
                            fifo_send_data_len  <= 0;
                    end
                    else begin
                        sdio_clk <= 1;
                    end
                end
                state_dat_write_chk:begin
                    timeout_set         <= 1;
                    sdio_clk            <= (clk_div_counter==clk_div)^sdio_clk;
                    sdio_dat_oe         <= 0;
                    if(sdio_clk&&(clk_div_counter==clk_div))begin
                        //数据检查
                        if(sdio_dat_write_chk[5]) //位为SxxxES------...E, S开始标志为0,xxx为状态码,E结束码=1,----如果为0,则表示忙碌,1则完成操作
                            sdio_dat_write_chk <= {sdio_dat_write_chk[4:0],sdio_dat[0]};
                        else
                            dat_check_done <= sdio_dat[0];           //直到被拉高
                    end
                end
                state_dat_block:begin
                    cmd_dat_wait         <= 1;
                    total_dat_remaining  <= total_dat_remaining - {16'd0,block_size};        //
                    wait_remaining       <= 5;
                    if(!total_dat_remaining[31])begin
                        //如果还有剩余数据,则重置CRC,实际上应该每个块都需要检查crc,暂时检查最后一个
                        sdio_dat_crc_rst    <= 1;
                    end
                    else begin
                        //最后一次读取数据,往fifo传输一个垃圾数据.外部可以直接判断emty状态机来读取数据
                        fifo_recv_wren_trg_nt <= ~fifo_recv_wren_trg_nt;
                    end
                end
                default:begin
                    if(crc_check>0)begin
                        //检查
                        crc_check       <= crc_check-1;
                        rsp_crc_fail    <= !rsp_crc_ok&&(crc_check==1);
                    end
                    sdio_clk        <= 1;
                    sdio_cmd_oe     <= 0;
                    sdio_dat_oe     <= 0;
                    cmd_start       <= 0;
                    cmd_send_done   <= cmd_send_done;
                    cmd_need_rsp    <= 0 ;
                    cmd_rsp_wait    <= 0 ;
                    cmd_rsp_done    <= 0 ;
                    //fifo_send_rden_shift <= 0;
                end
            endcase
        end
    end


    ///////////////////////////////////////////////////////必须的
    wire[7:0]   crc7;
    wire[47:0]  cmd_seq = {2'b01,cmd,argu,crc7};
    crc8_48 u_crc48(
        .data({2'b01,cmd,argu}),
        .crc(crc7)
    );

    wire[7:0]  rsp_crc7;
    crc8_48 u_rsp_crc(
        .data(rsp[47:8]),       //最后8位为效验码
        .crc(rsp_crc7)
    );

endmodule

module crc8 (
    data,
    crc,
    input_crc
);
    input [7:0] data;
    input [7:0] input_crc;
    output [7:0] crc;
    //data[0]?8'h09:8'h00
    wire[8:0] d  = {input_crc,1'b0} ^ data;
    wire[8:0] d7 = {d  ^ {4'd0, d[7],2'd0, d[7]},1'b0};
    wire[8:0] d6 = {d7 ^ {4'd0,d7[7],2'd0,d7[7]},1'b0};
    wire[8:0] d5 = {d6 ^ {4'd0,d6[7],2'd0,d6[7]},1'b0};
    wire[8:0] d4 = {d5 ^ {4'd0,d5[7],2'd0,d5[7]},1'b0};
    wire[8:0] d3 = {d4 ^ {4'd0,d4[7],2'd0,d4[7]},1'b0};
    wire[8:0] d2 = {d3 ^ {4'd0,d3[7],2'd0,d3[7]},1'b0};
    wire[8:0] d1 = {d2 ^ {4'd0,d2[7],2'd0,d2[7]},1'b0};
    wire[8:0] d0 = {d1 ^ {4'd0,d1[7],2'd0,d1[7]},1'b0};
    assign crc = d0[7:1];
endmodule

module crc8_48 (
    data,
    crc,
);
    input [39:0] data;
    output [7:0] crc;

    wire [7:0] crc0,crc1,crc2,crc3,crc4,crc5;
    //crc8 u_crc0(.data(data[6*8-1:5*8]),.crc(crc0),.input_crc(8'h00));
    crc8 u_crc1(.data(data[5*8-1:4*8]),.crc(crc1),.input_crc(8'h00));
    crc8 u_crc2(.data(data[4*8-1:3*8]),.crc(crc2),.input_crc(crc1));
    crc8 u_crc3(.data(data[3*8-1:2*8]),.crc(crc3),.input_crc(crc2));
    crc8 u_crc4(.data(data[2*8-1:1*8]),.crc(crc4),.input_crc(crc3));
    crc8 u_crc5(.data(data[1*8-1:0*8]),.crc(crc5),.input_crc(crc4));


    // function automatic [7:0] xcrc;
    //     input [7:0] crcIn;
    //     input [7:0] data;
    // begin
    //     xcrc[0] = crcIn[0] ^ crcIn[3] ^ data[0] ^ data[3];
    //     xcrc[1] = crcIn[1] ^ crcIn[4] ^ data[1] ^ data[4];
    //     xcrc[2] = crcIn[0] ^ crcIn[2] ^ crcIn[5] ^ data[0] ^ data[2] ^ data[5];
    //     xcrc[3] = crcIn[1] ^ crcIn[3] ^ crcIn[6] ^ data[1] ^ data[3] ^ data[6];
    //     xcrc[4] = crcIn[2] ^ crcIn[4] ^ crcIn[7] ^ data[2] ^ data[4] ^ data[7];
    //     xcrc[5] = crcIn[0] ^ crcIn[5] ^ data[0] ^ data[5];
    //     xcrc[6] = crcIn[1] ^ crcIn[6] ^ data[1] ^ data[6];
    //     xcrc[7] = crcIn[2] ^ crcIn[7] ^ data[2] ^ data[7];
    // end
    // endfunction
    //assign crc = xcrc(xcrc(xcrc(xcrc(xcrc(xcrc(8'h00,data[6*8-1:5*8]),data[5*8-1:4*8]),data[4*8-1:3*8]),data[3*8-1:2*8]),data[2*8-1:1*8]),data[1*8-1:0*8]);


    assign crc = {crc5[6:0],1'b1};
endmodule

module crc16_sig(
    crc_in,
    dat_i,
    crc_o,
    calc,
    clk,
    rst
);
    input[15:0]  crc_in;
    input        dat_i;
    output reg[15:0] crc_o;
    input        clk;
    input        rst;
    input        calc;

    wire[15:0]  polynomial = 16'h1021;
    wire[16:0]  crc_calc = {crc_o,1'b0} ^ (crc_o[15]?polynomial:16'h0000);

    always @(posedge clk or posedge rst) begin
        if(rst)begin
            crc_o <= 0;
        end
        else if(calc) begin
            crc_o <= crc_calc ^ (dat_i?polynomial:16'h0000);
        end
    end

endmodule

`ifdef SIM
module sim_tb ();
    //iverilog -DSIM sdio2.v && vvp a.out
    reg clk = 0;
    reg rst_n = 0;
    reg memory_clk = 0;
    wire clk50m = clk;

    always clk = #10 ~clk;
    always memory_clk = #1.25 ~memory_clk;
    always rst_n = #100 1;
    always #140000 $finish;
    reg[31:0] counter = 0;

    reg[31:0] apb_addr = 0;
    reg[31:0] apb_hwdata = 0;
    reg[3:0]  sdio_dat_test = 'hf;

    reg apb_hwrite = 0;
    reg apb_htran = 0;
    wire apb_hsel = apb_htran;
    wire apb_hready;
    reg sdio_cmd_test = 1;

    localparam test_counter = 10;

    reg[5:0]  cmd = 0;
    reg[31:0] argu= 0;
    wire cmd_en = cmd_en_trg ^ cmd_en_trg_nt;
    reg cmd_en_trg = 0;
    reg cmd_en_trg_nt = 0;
    wire[255:0] rsp;
    reg need_rsp = 0;
    reg long_rsp = 0;
    reg data_en = 0;
    reg data_write = 0;
    wire fifo_send_rden;
    reg [15:0] fifo_send_rden_num = 0;
    wire fifo_send_emty_clr = fifo_send_rden_num>=128*2;
    reg [15:0] fifo_recv_wren_num = 0;
    wire fifo_recv_wren;


    reg fifo_send_emty = 0;

`define counter_cmp(cnt) (counter==test_counter+cnt||counter==test_counter+cnt+1)
//`define TEST_WRITE

    always @(posedge clk) begin
        cmd_en_trg_nt <= cmd_en_trg;

        //if(apb_hready)
            counter <= counter + 1;

        if(fifo_send_rden)begin
            fifo_send_rden_num <= fifo_send_rden_num + 1;
        end

        if(fifo_recv_wren)
            fifo_recv_wren_num <= fifo_recv_wren_num + 1;

        if(counter==test_counter+0)begin

        end
        else if(counter==test_counter+3)begin
            cmd_en_trg_nt <= ~cmd_en_trg_nt;         //触发一次
            need_rsp   <= 0;
            // cmd        <= 8;
            // argu       <= 'h1aa;
            // data_en    <= 1;
            cmd        <= 0;
            argu       <= 0;
            need_rsp   <= 1;

            `ifdef TEST_WRITE
            data_en    <= 1;
            data_write <= 1;
            `else
            data_en    <= 0;
            `endif
        end
        // else if(
        //       `counter_cmp(2460)
        //     ||`counter_cmp(2462)
        //     ||`counter_cmp(2464)
        //     ||`counter_cmp(2466)
        //     //||`counter_cmp(2468)
        //     ||`counter_cmp(2470)
        //     ||`counter_cmp(2472)
        //     ||`counter_cmp(2474)
        //     ||`counter_cmp(2476)
        // )begin
        //     sdio_dat_test <= 'h0;
        // end
        else if(counter==test_counter+5)begin

        end
        else if(
            `counter_cmp(200)
        )
        begin
            fifo_send_emty <= 1;
        end
        else if(
            `counter_cmp(600)
        )
        begin
            fifo_send_emty <= 0;
        end
        else if(
            `counter_cmp(656)
        )
        begin
            fifo_send_emty <= 1;
        end
        else if(
            `counter_cmp(800)
        )
        begin
            fifo_send_emty <= 0;
        end
        else if(
            `counter_cmp(3000)
        )
            sdio_dat_test <= 0;
        else if(
            `counter_cmp(3002)
        )
            sdio_dat_test <= 'hf;
        else if(
            `counter_cmp(252)
        )
            sdio_cmd_test <= 0;
        else if(
            counter==test_counter+301||
            counter==test_counter+581
        )begin
            sdio_cmd_test          <= 1'b0;
        end
        else if(
            `counter_cmp(500)
        )begin
            sdio_dat_test          <= 4'h0;
        end
        else if(
            counter == test_counter+758     //读取
            ||counter == test_counter+358
            ||counter == test_counter+370
        )begin
        end
        `ifndef TEST_WRITE
        // else if(counter>=2560)begin
        //     sdio_dat_test <= 'h0;
        // end
        `else
        else if(counter>=2880)begin
            sdio_dat_test <= 'h0;
        end
        `endif
        else begin
            sdio_cmd_test <= 1;
            sdio_dat_test <= 'hf;
        end

    end

    sdio_fifo_top u_sdio(
        .clk        (clk),
        .rstn       (rst_n),
        .sdio_dat   (),
        .sdio_cmd   (),
        .sdio_clk   (),

        .cmd        (cmd        ),
        .argu       (argu       ),
        .cmd_en     (cmd_en     ),
        .rsp        (rsp        ),
        .need_rsp   (need_rsp   ),
        .long_rsp   (long_rsp   ),
        .data_en    (data_en    ),
        .data_len   (513),
        .block_size (512),
        .cmd_len    (48),
        .rsp_len    (48),
        .data_write (data_write ),
        .sdio_cmd_test(sdio_cmd_test),
        .sdio_dat_test(sdio_dat_test),
        .timeout_time ('d100_000_000),
        .clk_div(10),

        .fifo_send_emty      (fifo_send_emty|fifo_send_emty_clr),
        //.fifo_send_data_i    (counter),
        .fifo_send_data_i    ('hffffffff),      //验证效验码,0xeda9
        .fifo_send_rden      (fifo_send_rden),

        .fifo_recv_data_o    (),
        .fifo_recv_wren      (fifo_recv_wren),
        .fifo_recv_full      ()
    );

    initial begin
        $dumpfile("test.vcd");
        $dumpvars(0, sim_tb);

    end
endmodule
`endif

